package Gameplay.Projectile;

import java.awt.Rectangle;
import java.util.ArrayList;

import ADTs.PointADT;
import Gameplay.Level;
import Gameplay.OnScreenObject;
import Gameplay.Player;
import Gameplay.Enemy.Enemy;
import Project.Constants;
import Project.Main;
/**
 * This is class containing all kinds of information of each of 
 * the projectiles such as location, velocity, radius, movementBounds
 * and so on.
 * 
 * @author	Brian Wetherbee
 * 			Ryan McNamara
 * 			Zeng Yang
 *
 */
public class Projectile extends OnScreenObject {
	//@ public invariant Main.playerInLevel==true;
	
	public boolean friendly;//vertify if the projectile is targeting enemies or player.
	public double damage;//projectile's damage
	
	//@ requires vel != null;
	//@ requires loc != null;
	public Projectile(PointADT vel, PointADT loc) {
		this(vel,loc,true);
	}
	//@ requires vel != null;
	//@ requires loc != null;
	//@ requires friendly != null;
	public Projectile(PointADT vel, PointADT loc, boolean friendly){
		this(vel,loc,new PointADT(0, 0),friendly);
	}
	//@ requires vel != null;
	//@ requires loc != null;
	//@ requires acc != null;
	//@ requires friendly != null;
	public Projectile(PointADT vel, PointADT loc, PointADT acc, boolean friendly){
		this(vel,loc,acc,.9,friendly);
	}

	//@ requires vel != null;
	//@ requires loc != null;
	//@ requires acc != null;
	//@ requires friendly != null;
	//@ requires maxSpeed != null;
	public Projectile(PointADT vel, PointADT loc, PointADT acc, double maxSpeed, boolean friendly){
		this(vel,loc,acc,.9,3,friendly);
	}

	//@ requires vel != null;
	//@ requires loc != null;
	//@ requires acc != null;
	//@ requires friendly != null;
	//@ requires maxSpeed != null;
	//@ ensures this.velocity == vel;
	//@ ensures this.location == loc;
	//@ ensures this.movementBounds.height == Constants.height;
	//@ ensures this.movementBounds.width == Constants.width;
	//@ ensures this.radius != 0;
	//@ ensures this.maxSpeed == maxSpeed;
	//@ ensures this.damage == damage;
	//@ ensures this.friendly == friendly;
	//@ ensures this.currLevel = (Level)Main.getCurrentUI();
	public Projectile(PointADT vel, PointADT loc, PointADT acc, double maxSpeed, double damage, boolean friendly){
		currLevel = (Level)Main.getCurrentUI();
		setAcceleration(new PointADT(0,0));
		maxAcceleration = .1;
		this.velocity = vel;
		this.location = loc;
		this.movementBounds = new Rectangle(0,0,Constants.width,Constants.height);
		this.radius = 4;
		this.maxSpeed = maxSpeed;
		this.damage = damage;
		this.friendly = friendly;
	}
	
	
	
	/*@ assignable this.location;
	  @ ensures true;
	  @*/
	public boolean move(){
		if(!super.move()){
			remove();
			return false;
		}
		OnScreenObject o = detectObjectCollision();
		handleObjectCollision(o);
		return true;
	}
	
	public OnScreenObject detectObjectCollision(){
		ArrayList<OnScreenObject> objlist = currLevel.getObjectList();
		for(int i = 0; i < objlist.size();i++){
			OnScreenObject o = objlist.get(i);
			if(o != this &&	PointADT.distance(o.getLocation(), this.location) < o.getRadius()+this.getRadius()){
				return o;
			}
		}
		return null;
	}
	/**
	 * This method is to decide if the projectile should make damage on 
	 * the objects based on the boolean value of "friendly".
	 * 
	 * friendly = true implies takeDamage from enemy.
	 * friendly = false implies takeDamage from player.
	 * @param o
	 */
	public void handleObjectCollision(OnScreenObject o){
		if(o != null){
			if(this.friendly){
				if(o instanceof Enemy){
					((Enemy)o).damage(this.computeDamage(o.getLocation().copy()));
					this.remove();
				}

			}
			else{
				if(o instanceof Player){

					((Player)o).damage(this.computeDamage(o.getLocation().copy()));
					this.remove();
				}
			}
		}
	}
	
	

	public double computeDamage(PointADT point){
		return damage;
	}
	
	/*@ requires ((Level)Main.CurrentUI).state.getProjectileList().contains(this);
	 *@ ensures ((Level)Main.CurrentUI).state.getProjectileList().size() == \old(((Level)Main.CurrentUI).state.getProjectileList().size()-1)
	 */
	public void remove(){
		currLevel.removeFromState(this);
	}
	
}
